
package at.fhj.ase.crypto.client.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.slf4j.Logger;

import at.fhj.ase.crypto.common.util.CommandBuilder;
import at.fhj.ase.crypto.common.util.LogUtil;
import at.fhj.ase.crypto.common.util.StringUtil;

/**
 * Class implementing the console reading tasks.
 * <p>
 * At first, the username is read in. As long as there is not provided a username, the process calls itsself recursively.<br>
 * As soon be have a valid username, a beautified username is created looking like "[ username ]".<br>
 * This beautified username is then also sent over the network in front of the chat message.
 * <p>
 * If a valid chat message has been provided by the user ({@link String#length()} 0 < x < 50), it is directly sent to server calling
 * {@link ServerConnector#sendEncryptedData(String)}.<br>
 * This method automatically uses the correct protocol key of {@link CommandBuilder} and fully encrypts the message.<br>
 * We could therefore also say: {@link ConsoleReaderThread} and {@link ServerConnector} work together.
 * <p>
 * Note that this class has to be started manually calling {@link #start()}. There is no protection against multiple threads.<br>
 * Note also that this class does not accept empty lines to be sent to the server. A line must contain at least one char.
 * 
 * @author sfe | www.fh-joanneum.at | Practical Aspects of Cryptography
 */
public class ConsoleReaderThread implements Runnable {
  /** The logging instance */
  private final Logger logger = LogUtil.getLogger(this);
  /** The reader we read the user input from (console) */
  private final BufferedReader consoleReader;
  /** The {@link ServerConnector} instance that is our access point for sending messages to the server the user has typed on console */
  private final ServerConnector serverConnector;
  /** The client username that has been supplied by the user (surely not empty and not null) */
  private String clientUserName;
  /** The beautified user name that is appended in front of the chat messages, looking like "[ <code>clientUserName</code> ]" */
  private String beautifiedUserName;

  /**
   * Constructor creating a new instance with the provided {@link ServerConnector} as message interface (to send messages)
   * 
   * @param serverConnector the {@link ServerConnector} instance we want to send the typed user chat messages to
   */
  public ConsoleReaderThread(ServerConnector serverConnector) {
    this.serverConnector = serverConnector;

    logger.debug("Initiating console reader...");
    consoleReader = new BufferedReader(new InputStreamReader(System.in));
  }

  /**
   * Handles the actual reading of the console and the sending to the {@link ServerConnector} instance.<br>
   * The message is assembled like that: beaufified user name, {@link CommandBuilder#SPACE} and the read content of console.<br>
   * An emtpy message is not send to server.<br>
   * All the actions and read messages are logged by this logging instance.<br>
   * If an exception occurs, the current Thread is interrupted and this method returns.
   */
  @Override
  public void run() {
    try {
      String readLine;
      while (((readLine = consoleReader.readLine()) != null) && !Thread.currentThread().isInterrupted()) {
        logger.debug("Read line: " + readLine);

        if (StringUtil.isEmptyOrNull(readLine)) {
          logger.warn("Do not send message to server, emtpy...");
        } else {
          logger.debug("Sending line to server...");
          serverConnector.sendEncryptedData(beautifiedUserName + CommandBuilder.SPACE + readLine);
        }
      }
    } catch (IOException e) {
      logger.error("Could not read line from console", e);
      Thread.currentThread().interrupt();
      return;
    }
  }

  /**
   * Starts this thread by first reading in the username.
   * <p>
   * This is done recursively as long as a valid username has been provided (length 0 < x < 50).<br>
   * Then, the beautified user name is created out of username (enclosing brackets).<br>
   * <p>
   * After successfully reading and processing the username, the thread is started awaiting further console input and sending this input to the server.<br>
   * Note that this method does not prevent multiple instances/threads of {@link ConsoleReaderThread} as the thread is not saved locally.
   */
  void start() {
    logger.debug("Trying to read in and beautify username...");
    clientUserName = readUserName();
    beautifiedUserName = "[" + CommandBuilder.SPACE + clientUserName + CommandBuilder.SPACE + "]";
    logger.debug("Successfully read in username, result: " + clientUserName + ", usage at chat-msgs: " + beautifiedUserName);

    System.out.println("You can now type chat messages, your username will be: " + beautifiedUserName);

    logger.debug("Starting thread...");
    new Thread(this).start();
  }

  /**
   * Reads in the user name from console (Std.in).<br>
   * As long as the username does not have at least one character, it is recursively called.<br>
   * The username must also not exceed 50 chars.
   */
  private String readUserName() {
    final String LOG_MSG = "Please type username: ";
    logger.info(LOG_MSG);
    System.out.print(LOG_MSG);

    try {
      // no empty string allowed, length from 1-50
      final String userName = StringUtil.checkLengthDefaults(consoleReader.readLine());
      return userName;
    } catch (Exception e) {
      logger.error("Could not read username, retrying...", e);
      return readUserName();
    }
  }
}
